#include <GameEngine/Graphics/Meshes/trgl_mesh.h>

#include <GameEngine/Graphics/Meshes/trgl_mesh_io.h>
#include <GameEngine/Graphics/openGL.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================

//! Default constructor
TrglMesh::TrglMesh() {}

//! Destructor
TrglMesh::~TrglMesh() {}

//! Render the mesh
/*!
 @param ray The picking ray under the mouse
 @param transparency Determines whether transparent or opaque graphic primitives should be drawn
 @return True if some triangles were rendered, false otherwise
 */
bool TrglMesh::render_mesh(const Line3d<float>& ray, bool transparency) {
	// Iterate over the triangle groups
	bool res = false;
	for ( std::vector<TrglMeshGroup>::iterator it = trgl_groups_.begin(); it != trgl_groups_.end(); ++it ) {
		res |= it->render_group(ray,*this,transparency);
	}
	return res;
}

//! Merge the rhs mesh with this mesh
/*!
 @param rhs The other mesh to merge with this mesh
 */
void TrglMesh::merge(const TrglMesh& rhs) {
	// Add vertices
	size_t vertices_count = vertices_.size();
	for ( std::vector< Point3d<float> >::const_iterator it = rhs.vertices_.begin(); it != rhs.vertices_.end(); ++it ) {
		vertices_.push_back(*it);
	}

	// Add normals
	size_t normals_count = normals_.size();
	for ( std::vector< Vector3d<float> >::const_iterator it = rhs.normals_.begin(); it != rhs.normals_.end(); ++it ) {
		normals_.push_back(*it);
	}

	// Add texture coordinates
	size_t coords_count = coords_.size();
	for ( std::vector< TextureCoordinate2d >::const_iterator it = rhs.coords_.begin(); it != rhs.coords_.end(); ++it ) {
		coords_.push_back(*it);
	}

	// Create a set of group names
	std::set<GEString> names;
	for ( std::vector<TrglMeshGroup>::const_iterator it = trgl_groups_.begin(); it != trgl_groups_.end(); ++it ) {
		names.insert( it->name() );
	}

	// Add triangles
	for ( std::vector<TrglMeshGroup>::const_iterator it = rhs.trgl_groups_.begin(); it != rhs.trgl_groups_.end(); ++it ) {
		TrglMeshGroup cur = *it;

		// Iterate over the subgroups
		for ( std::vector<TrglMeshSubGroup>::iterator it2 = cur.subgroups().begin(); it2 != cur.subgroups().end(); ++it2 ) {
			TrglMeshSubGroup& cur_sub = *it2;

			// Iterate over the triangles
			for ( std::vector< IndexedTrgl >::iterator it3 = cur_sub.triangles().begin(); it3 != cur_sub.triangles().end(); ++it3 ) {
				IndexedTrgl& trgl = *it3;
				trgl.index1() += (int)vertices_count;
				trgl.index2() += (int)vertices_count;
				trgl.index3() += (int)vertices_count;
				if ( trgl.has_normals() ) {
					trgl.nindex1() += (int)normals_count;
					trgl.nindex2() += (int)normals_count;
					trgl.nindex3() += (int)normals_count;
				}
				if ( trgl.has_texture_coordinates() ) {
					trgl.cindex1() += (int)coords_count;
					trgl.cindex2() += (int)coords_count;
					trgl.cindex3() += (int)coords_count;
				}
			}
		}

		// Add the triangle group
		if ( names.find(cur.name()) != names.end() ) {
			// Find the group in this mesh
			bool found = false;
			for ( std::vector<TrglMeshGroup>::iterator it = trgl_groups_.begin(); it != trgl_groups_.end(); ++it ) {
				if ( it->name() != cur.name() ) continue;

				// Iterate over the subgroups
				for ( std::vector<TrglMeshSubGroup>::iterator it2 = cur.subgroups().begin(); it2 != cur.subgroups().end(); ++it2 ) {
					it->subgroups().push_back(*it2);
				}

				// Leave loop
				found = true;
				break;
			}

			// If no such group was found, add the group
			if ( found = false ) trgl_groups_.push_back(cur);
		}
		else {
			trgl_groups_.push_back(cur);
		}
	}
}

//! Add the given subgroup to the given group of triangles in the mesh
/*!
 @param subgroup The subgroup to add
 @param group_name The name of the group that should handle the subgroup. If there
                   is no such group in the mesh, the group is created.
 */
void TrglMesh::add_subgroup(const TrglMeshSubGroup& subgroup, const GEString group_name) {
	// Try to find the group
	for ( std::vector<TrglMeshGroup>::iterator it = trgl_groups_.begin(); it != trgl_groups_.end(); ++it ) {
		// Check group name
		if ( it->name() != group_name ) continue;

		// Add subgroup
		it->subgroups().push_back(subgroup);
		return;
	}

	// No such group was found
	TrglMeshGroup group; group.name() = group_name;
	group.subgroups().push_back(subgroup);
	trgl_groups_.push_back(group);
}

//! Load the mesh from an OBJ file
/*!
 @param file The path to the OBJ file
 @param scale Rescale the vertex positions of the mesh
 @return True if mesh could be loaded, false otherwise
 */
bool TrglMesh::load_obj_file(const GEString& file, float scale) {
	return TrglMeshIO::load_obj_file(*this,file,scale);
}

//! Save the mesh to an OBJ file
/*!
 @param file The path to the OBJ file
 @return True if mesh could be saved, false otherwise
 */
bool TrglMesh::save_obj_file(const GEString& file) const {
	return TrglMeshIO::save_obj_file(*this,file);
}

//! Load the mesh from a binary file
/*!
 @param file The path to the binary file
 @param scale Rescale the vertex positions of the mesh
 @return True if mesh could be loaded, false otherwise
 */
bool TrglMesh::load_bin_file(const GEString& file, float scale) {
	return TrglMeshIO::load_bin_file(*this,file,scale);
}

//! Save the mesh to a binary file
/*!
 @param file The path to the binary file
 @return True if mesh could be saved, false otherwise
 */
bool TrglMesh::save_bin_file(const GEString& file) const {
	return TrglMeshIO::save_bin_file(*this,file);
}

//! Load the mesh from a NIF file (NetImmerse File)
/*!
 *  This function uses the Niflib library to load the NIF file, provided with the following license:
 *
 @verbatim
  Copyright (c) 2005, NIF File Format Library and Tools
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

     * Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

     * Redistributions in binary form must reproduce the above
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.

     * Neither the name of the NIF File Format Library and Tools
       project nor the names of its contributors may be used to endorse
       or promote products derived from this software without specific
       prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. @endverbatim
 @param file The path to the NIF file
 @param scale Rescale the vertex positions of the mesh
 @return True if mesh could be loaded, false otherwise
 */
bool TrglMesh::load_nif_file(const GEString& file, float scale) {
	return TrglMeshIO::load_nif_file(*this,file,scale);
}

//! Save the mesh to a NIF file (NetImmerse File)
/*!
 *  This function uses the Niflib library to save the NIF file, provided with the following license:
 *
 @verbatim
  Copyright (c) 2005, NIF File Format Library and Tools
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

     * Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

     * Redistributions in binary form must reproduce the above
       copyright notice, this list of conditions and the following
       disclaimer in the documentation and/or other materials provided
       with the distribution.

     * Neither the name of the NIF File Format Library and Tools
       project nor the names of its contributors may be used to endorse
       or promote products derived from this software without specific
       prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. @endverbatim
 @param file The path to the NIF file
 @return True if mesh could be saved, false otherwise
 */
bool TrglMesh::save_nif_file(const GEString& file) const {
	return TrglMeshIO::save_nif_file(*this,file);
}

//! Load the mesh from a 3DS file (3D Studio Max File)
/*!
 *  This function uses the lib3ds library to load the 3DS file, provided under the GNU GPL 2.0 license.
 *
 @param file The path to the 3DS file
 @param scale Rescale the vertex positions of the mesh
 @return True if mesh could be loaded, false otherwise
 */
bool TrglMesh::load_3ds_file(const GEString& file, float scale) {
	return TrglMeshIO::load_3ds_file(*this,file,scale);
}

//! Save the mesh to a 3DS file (3D Studio Max File)
/*!
 *  This function uses the lib3ds library to save the 3DS file, provided under the GNU GPL 2.0 license.
 *
 @param file The path to the 3DS file
 @return True if mesh could be saved, false otherwise
 */
bool TrglMesh::save_3ds_file(const GEString& file) const {
	return TrglMeshIO::save_3ds_file(*this,file);
}

//! Accessor to the number of vertices in the mesh
/*!
 @return The number of vertices in the mesh
 */
SurfaceMesh::VertexCount TrglMesh::vertex_count() const {
	return (SurfaceMesh::VertexCount)vertices_.size();
}

//! Accessor to the number of normals in the mesh
/*!
 @return The number of normals in the mesh
 */
SurfaceMesh::NormalCount TrglMesh::normal_count() const {
	return (SurfaceMesh::NormalCount)normals_.size();
}

//! Accessor to the number of texture coordinates in the mesh
/*!
 @return The number of texture coordinates in the mesh
 */
SurfaceMesh::TextureCoordinateCount TrglMesh::texture_coordinate_count() const {
	return (SurfaceMesh::TextureCoordinateCount)coords_.size();
}

//! Accessor to the number of faces in the mesh
/*!
 @return The number of faces in the mesh
 */
SurfaceMesh::FaceCount TrglMesh::face_count() const {
	SurfaceMesh::FaceCount res = 0;

	// Iterate over the triangle groups
	for ( std::vector<TrglMeshGroup>::const_iterator it = trgl_groups_.begin(); it != trgl_groups_.end(); ++it ) {
		res += it->face_count();
	}

	// Return result
	return res;
}

//=======================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
